package cartagows;


import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import javax.wsdl.Definition;
import javax.wsdl.Input;
import javax.wsdl.Operation;
import javax.wsdl.OperationType;
import javax.wsdl.PortType;
import javax.wsdl.Service;
import javax.wsdl.factory.WSDLFactory;
import javax.wsdl.xml.WSDLReader;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.OMNamespace;
import org.apache.axiom.om.OMXMLParserWrapper;
import org.apache.axiom.om.impl.llom.factory.OMXMLBuilderFactory;
import org.apache.axiom.om.impl.llom.util.AXIOMUtil;
import org.apache.axiom.soap.SOAPBody;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axis2.AxisFault;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.Options;
import org.apache.axis2.client.ServiceClient;
import org.apache.axis2.saaj.util.SAAJUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.neethi.All;
import org.apache.neethi.Constants;
import org.apache.neethi.ExactlyOne;
import org.apache.neethi.Policy;
import org.apache.neethi.PolicyEngine;
import org.apache.neethi.PolicyReference;
import org.apache.ws.security.message.WSSecHeader;
import org.w3c.dom.Document;

import cartago.ARTIFACT_INFO;
import cartago.Artifact;
import cartago.CartagoException;
import cartago.GUARD;
import cartago.OPERATION;
import cartago.OUTPORT;
import cartago.OpFeedbackParam;
import cartagows.util.SOAPUtil;
import cartagows.util.XMLib;
import cartagows.wsframework.core.DataContainer;
import cartagows.wsframework.core.IOperation;
import cartagows.wsframework.core.ServiceConfiguration;
import cartagows.wsframework.wsaddressing.WSAddressingParam;
import cartagows.wsframework.wssecurity.core.AsymmetricBindingBuilder;
import cartagows.wsframework.wssecurity.core.MessageData;
import cartagows.wsframework.wssecurity.core.SecurityException;
import cartagows.wsframework.wssecurity.core.SecurityInfo;
import cartagows.wsframework.wssecurity.core.ValidationResults;
import cartagows.wsframework.wssecurity.core.WSSecurityOperation;
import cartagows.wsframework.wssecurity.core.WSSecurityOperationContent;
import cartagows.wsframework.wssecurity.policy.PolicyBuilder;
import cartagows.wsframework.wssecurity.policy.PolicyData;
import cartagows.wsframework.wssecurity.policy.WSSPolicyException;
import cartagows.wsframework.wssecurity.utils.SecurityUtil;

import com.ibm.wsdl.PortImpl;
import com.ibm.wsdl.extensions.soap.SOAPBindingImpl;

@ARTIFACT_INFO( outports = { @OUTPORT(name = "out-port") } )
	
public class WSInterface extends Artifact {

	private static final Log log = LogFactory.getLog(WSInterface.class);
	private HashMap<String,LinkedList<MsgInfo>> msgs;
	private ServiceConfiguration configuration;

	private EndpointReference targetEPR;
	private EndpointReference localEPR;
	private EndpointReference securityTargetEPR;
	private OMFactory fac;
	private OMNamespace wsaNS;
	private boolean wsAddressingSupport;
	private String portTypeName;

	private boolean wsHeaderSupport;

	private Definition def;
	
	/**
	 * Stores the operations provided by the WS we want to reach:
	 * 
	 *  - HashMap key: The operation name provided by the WS. Used to 
	 *    automatically retrieve, from the operation name, the action 
	 *    associated to this operation (used by WSPanel for checking if 
	 *    the arriving message is valid for the WS or not).
	 *  - HashMap value: An entry containing the operation action (see before)
	 *    and the message name (used as a unique wrapper of the message contents
	 *    in case of xxxWithParts operations)
	*/
	private HashMap<String, WSInterface.MsgOpMapEntry> msgOpMap;
	
	private String defaultBaseLocalEndp = CartagowsParam.BASE_SERVICE_PATH;
	private WSDLFactory wsdlfactory;
	private WSDLReader wsdlreader;
	private SOAPFactory factory = OMAbstractFactory.getSOAP12Factory();
	private HashMap<String,PolicyData> policyRequestDataMap;
	private HashMap<String,PolicyData> policyResponseDataMap;


	/**
	 *  Initialization of the artifact with the parameters given in input
	 * @param wsdlURI
	 * @param serviceName
	 * @param port
	 * @throws Exception
	 */
	@OPERATION void init(String wsdlURI, String serviceName, String port) throws Exception{
		//String realWsdlURI = CWSServiceManager.CARTAGO_WS_BASE_PATH + wsdlURI;
		//this.commonInitStep(realWsdlURI);
		this.commonInitStep(wsdlURI);
		portTypeName = port;
		parseWSDL(serviceName, portTypeName);
		this.securityTargetEPR = targetEPR;
		//parseSecInfo(realWsdlURI);
		parseSecInfo(wsdlURI);
	}

	/**
	 *  Initialization of the artifact with the parameters given in input. The WSInterface
	 *  will be instantiated with the first service and port founded in the WSDL. This initialization
	 *  method should be used only when the WSDL contains a unique Service with a unique PortType.
	 *  
	 * @param wsdlURI
	 * @throws Exception
	 */
	@OPERATION void init(String wsdlURI) throws Exception{
		//String realWsdlURI = CWSServiceManager.CARTAGO_WS_BASE_PATH + wsdlURI;
		//this.commonInitStep(realWsdlURI);
		this.commonInitStep(wsdlURI);
		parseWSDL();
		this.securityTargetEPR = targetEPR;
		//parseSecInfo(realWsdlURI);
		parseSecInfo(wsdlURI);
	}

	/**
	 *  Initialization of the artifact with the parameters given in input. The WSInterface will be instantiated
	 *  with the operation founded in the portType specified in input. This initialization method should be used
	 *  only when the communication between service is based upon some WS-* specification (WS-Coordination for example).
	 *  The operation supported by the service are specified by the portType given in
	 *  input and the recipient of the message is the targetEpr also specified in input.   
	 * @param wsdlURI
	 * @param portType
	 * @param targetEpr
	 * @throws Exception
	 */
	@OPERATION void init(String wsdlURI, String port, EndpointReference targetEpr, Boolean fake) throws Exception{
		//String realWsdlURI = CWSServiceManager.CARTAGO_WS_BASE_PATH + wsdlURI;
		//this.commonInitStep(realWsdlURI);
		this.commonInitStep(wsdlURI);
		this.targetEPR = targetEpr;
		portTypeName = port;
		parseWSDL(portTypeName, targetEpr);
		this.securityTargetEPR = targetEpr;
		parseSecInfo(wsdlURI);
	}
	/**
	 * Common utility method for the initialization
	 * @param wsdlURI
	 * @throws Exception
	 */
	private void commonInitStep(String wsdlURI)throws Exception{
		msgs = new HashMap<String,LinkedList<MsgInfo>>();
		localEPR = new EndpointReference(defaultBaseLocalEndp+"/"+this.getId().getName());
		fac = OMAbstractFactory.getOMFactory();
		wsaNS = fac.createOMNamespace(WSAddressingParam.NAMESPACE, WSAddressingParam.NAMESPACE_PREFIX);
		wsAddressingSupport = true;
		wsHeaderSupport = true;
		msgOpMap = new HashMap<String,WSInterface.MsgOpMapEntry>();

		wsdlfactory = WSDLFactory.newInstance();
		wsdlreader = wsdlfactory.newWSDLReader();
		def = wsdlreader.readWSDL(wsdlURI);	

		policyRequestDataMap = new HashMap<String, PolicyData>();
		policyResponseDataMap = new HashMap<String, PolicyData>();

	}

	private void parseWSDL(String serviceName, String portType) throws Exception{

		Map portsMap = null;

		//Service search
		Collection services = def.getAllServices().values();
		for (Object obj: services){
			Service srv = (Service)obj;
			if (srv.getQName().getLocalPart().equals(serviceName)){
				portsMap = srv.getPorts();
				break;
			}
		}
		//No ports founded for the service
		if (portsMap==null)
			throw new Exception("No ports find for the given service");

		//Ports successfully founded
		else{
			for (Object obj: portsMap.values()){
				PortImpl pt = (PortImpl)obj;

				//Check for the portType provided in input by the user.
				if(!pt.getName().equals(portType)) continue;
				else{

					//Parse of the operation specified in the portType
					//Search of the targetEPR if not already setted by the init operation
					if(targetEPR==null){
						List elems = pt.getExtensibilityElements();
						for (Object obj2: elems){
							if (obj2 instanceof javax.wsdl.extensions.soap.SOAPAddress){
								javax.wsdl.extensions.soap.SOAPAddress el = (javax.wsdl.extensions.soap.SOAPAddress) obj2;
								targetEPR = new EndpointReference(el.getLocationURI());
							}
						}
					}

					parsePortOperations(pt);
					break;
				}
			}
			//Port not found
			if(msgOpMap==null)
				throw new Exception();
		}
	}

	private void parseWSDL() throws Exception{

		PortImpl pt = null;
		Collection services;

		services = def.getAllServices().values();

		//Search of the first port of the first service
		for (Object obj: services){
			Service srv = (Service)obj;
			pt = (PortImpl) srv.getPorts().values().iterator().next();
			break;
		}

		//No port found
		if(pt==null)
			throw new Exception();
		//Port found
		else{

			if(targetEPR==null){
				List elems = pt.getExtensibilityElements();
				for (Object obj2: elems){
					if (obj2 instanceof javax.wsdl.extensions.soap.SOAPAddress){
						javax.wsdl.extensions.soap.SOAPAddress el = (javax.wsdl.extensions.soap.SOAPAddress) obj2;
						targetEPR = new EndpointReference(el.getLocationURI());
					}
				}
			}
			//Parse of the operation specified in the portType
			parsePortOperations(pt);
		}
	}


	private void parseWSDL(String portType, EndpointReference targetEpr) throws Exception{

		Map portsMap = def.getAllPortTypes();

		for(Object obj: portsMap.values()){
			//PortType pt = (PortType)obj;
			PortType pt = (PortType)obj;
			//Search for the portType given in input
			if(pt.getQName().getLocalPart().equals(portType)){
				//Parse of the operation of the portType
				parsePortOperations(pt, true);
				break;
			}
		}
		//No PortType found
		if (msgOpMap == null)
			throw new Exception();

	}


	private void parsePortOperations(PortType pt, boolean isBindingRpc){

		String opInAction;
		QName opInMsgName;
		Input in;
		
    	for (Object obj1: pt.getOperations()){
    		Operation op = (Operation)obj1;
    		//log.info("Op: "+op.getName());
    		String base;
    		
			/** 
			  * Secondo specifiche le action per i messaggi relativi a WS-AtomicTransaction 
			  * devono avere una precisa struttura e pertanto viene costruita manulmente.
			  * (il namespace cioe' deve rimanere http://docs.oasis-open.org/ws-tx/wsat/2006/06)
			  * 
			**/ 
			if(op.getName().equals("PrepareOperation") ||op.getName().equals("CommitOperation") || op.getName().equals("RollbackOperation") || op.getName().equals("AbortedOperation") || op.getName().equals("CommittedOperation") || op.getName().equals("PreparedOperation") || op.getName().equals("ReadOnlyOperation"))
				base = "http://docs.oasis-open.org/ws-tx/wsat/2006/06"+"/";
			else
				base = def.getTargetNamespace()+"/"+pt.getQName().getLocalPart()+"/";
    	}

		/*
		 * Construction of the operation's action in conformance with the WS-Adrressing specification
		 * In the absence of the wsa:Action attribute, the following pattern is used 
		 * to construct a default action for inputs and outputs. The general form of an action URI is as follows:
		 * 
		 * [target namespace]/[port type name]/[input|output name]
		 * 
		 * where [input|output name] it is a parameter which provides a unique name among all input and output elements within the enclosing port type.
		 * 
		 * In order to avoid having to name each input and output element within an operation, WSDL provides some default values based on the operation name. 
		 * If the name attribute is not specified on a one-way or notification message, it defaults to the name of the operation. If the name attribute is 
		 * not specified on the input or output messages of a request-response or solicit-response operation, the name defaults to the name of the operation with 
		 * "Request"/"Solicit" or "Response" appended, respectively. 
		 */

    	for (Object obj1: pt.getOperations()){
    		Operation op = (Operation)obj1;
    		//log.info("Op: "+op.getName());
    		String base;
			if(op.getName().equals("PrepareOperation") ||op.getName().equals("CommitOperation") || op.getName().equals("RollbackOperation") || op.getName().equals("AbortedOperation") || op.getName().equals("CommittedOperation") || op.getName().equals("PreparedOperation") || op.getName().equals("ReadOnlyOperation"))
				base = "http://docs.oasis-open.org/ws-tx/wsat/2006/06"+"/";
			else
				base = def.getTargetNamespace()+"/"+pt.getQName().getLocalPart()+"/";
			
			

    		if (op.getStyle().equals(OperationType.ONE_WAY)){
    			
    			in = op.getInput();
				//Retrieval of the messageInputName (the root of the SOAP message body related to the operation)
				opInMsgName = getInOpMsgName(isBindingRpc, op);

    			if (in.getName()!=null){
    				opInAction = base+in.getName();
    				msgOpMap.put(op.getName(), new MsgOpMapEntry(opInAction, opInMsgName));
    				//log.info("link: "+base+in.getName()+"-->"+op.getName());
    			} else {
    				opInAction = base+op.getName();
    				msgOpMap.put(op.getName(), new MsgOpMapEntry(opInAction, opInMsgName));
    				//log.info("link: "+base+op.getName()+"-->"+op.getName());
    			}
    		} else if (op.getStyle().equals(OperationType.REQUEST_RESPONSE)){
    			
    			in = op.getInput();
				//Retrieval of the messageInputName (the root of the SOAP message body related to the operation)
				opInMsgName = getInOpMsgName(isBindingRpc, op);
    			
    			if (in.getName()!=null){
    				opInAction = base+in.getName();
    				msgOpMap.put(op.getName(), new MsgOpMapEntry(opInAction, opInMsgName));
    				//log.info("link: "+base+in.getName()+"-->"+op.getName());
    			} else {
    				opInAction = base+op.getName()+"Request";
    				msgOpMap.put(op.getName(), new MsgOpMapEntry(opInAction, opInMsgName));
    				//log.info("link: "+base+op.getName()+"Request-->"+op.getName());
    			}
    			/*Output out = op.getOutput();
    			if (out.getName()!=null){
    				msgOpMap.put(op.getName(),base+out.getName());
    				//log.info("link: "+base+out.getName()+"<--"+op.getName());
    			} else {
    				msgOpMap.put(op.getName(),base+op.getName()+"Response");
    				//log.info("link: "+base+op.getName()+"Response<--"+op.getName());
    			}*/
    		} /*else if (op.getStyle().equals(OperationType.NOTIFICATION)){
    			Output out = op.getOutput();
    			if (out.getName()!=null){
    				msgOpMap.put(op.getName(),base+out.getName());
    				//log.info("link: "+base+out.getName()+"<--"+op.getName());
    			} else {
    				msgOpMap.put(op.getName(),base+op.getName());
    				//log.info("link: "+base+op.getName()+"<--"+op.getName());
    			}
    		} else if (op.getStyle().equals(OperationType.SOLICIT_RESPONSE)){
    			// TODO
    		}*/
    	}

	}
	
	
	private void parsePortOperations(PortImpl ptImpl){

		String opInAction;
		QName opInMsgName;
		Input in;
		boolean isBindingRpc = false;
		PortType pt = ptImpl.getBinding().getPortType();
		//Founding the binding type
		for (Object el : ptImpl.getBinding().getExtensibilityElements()){
			if (el instanceof SOAPBindingImpl){
				SOAPBindingImpl bindingImpl = (SOAPBindingImpl) el;
				if(bindingImpl.getStyle().equals(CartagowsParam.BINDING_RPC)) isBindingRpc = true;
				break;
			}
		}

		this.parsePortOperations(ptImpl.getBinding().getPortType(), isBindingRpc);
	}


	private void sendSOAPMsg(String wsAction, String msgBody, String relatedTo, String replyAddress, OpFeedbackParam<String> sentMsgId){
		try {		
			//log.info("new request "+opName);
			Options options = new Options();
			ServiceClient sender = new ServiceClient();
			sender.setOptions(options);
			options.setTo(targetEPR);

			UUID id = UUID.randomUUID();
			String ids = "uuid:"+id;

			if (wsHeaderSupport){
				OMElement to = fac.createOMElement("To", wsaNS);
				to.setText(targetEPR.getAddress());
				sender.addHeader(to);

				options.setAction("");
				OMElement action = fac.createOMElement("Action", wsaNS);
				action.setText(wsAction);
				sender.addHeader(action);

				//log.info("RelatesTo: "+relates);

				OMElement msgId = fac.createOMElement("MessageID", wsaNS);
				msgId.setText(ids);
				sender.addHeader(msgId);

				OMElement from = fac.createOMElement("From", wsaNS);
				from.setText(localEPR.getAddress());
				sender.addHeader(from);

				if (replyAddress!=null){
					OMElement replyTo = fac.createOMElement("ReplyTo", wsaNS);
					OMElement replyToAddress= fac.createOMElement("Address", wsaNS);
					replyToAddress.setText(replyAddress);
					replyTo.addChild(replyToAddress);
					sender.addHeader(replyTo);

				}

				if (relatedTo!=null){
					OMElement relates = fac.createOMElement("RelatesTo", wsaNS);
					relates.setText(relatedTo);
					sender.addHeader(relates);
				}

				//The configuration information setted for this WSInterface will be added to
				//the header of the msg
				/*if(configuration!=null){
					if(configuration.isWsCoordConfigPresent())
						sender.addHeader(configuration.getWsCoordinationConf());

					if(configuration.isWsSecurityConfigPresent())
						sender.addHeader(configuration.getWsSecurityConf());

				}*/
				
				if(configuration!=null){
					OMElement[] headersList = configuration.getConfig();
					for(int i=0;i<headersList.length;i++){
						sender.addHeader(headersList[i]);
					}
				}
				msgs.put(ids,new LinkedList<MsgInfo>());
			}
			try {
				OMElement el = AXIOMUtil.stringToOM(msgBody);
				sender.sendRobust(el);
				sentMsgId.set(ids);
			} catch (Exception ex){
				failed(ex.toString());
			}

		} catch (AxisFault ex){
			failed(ex.toString());
		}
	}

	@OPERATION void setLocalEPR(String localEPR){
		this.localEPR = new EndpointReference(localEPR);
	}

	@OPERATION void setTargetEPR(String targetEPR){
		this.targetEPR = new EndpointReference(targetEPR);
	}
	
	@OPERATION void setSecurityTargetEPR(String securityTargetEPR){
		this.securityTargetEPR = new EndpointReference(securityTargetEPR);
	}

	@OPERATION void setLocalEPRBaseAddress(String baseLocalEPR){
		localEPR = new EndpointReference(baseLocalEPR+"/"+this.getId().getName());
	}

	@OPERATION void setWSAddressingSupport(boolean flag){
		this.wsAddressingSupport = flag;
	}

	@OPERATION void setWSHeaderSupport(boolean support){
		wsHeaderSupport = support;
	}


	// simple MEP

	@OPERATION void oneWayOp(String opName, String msgBody, OpFeedbackParam<String> sentMsgId) throws CartagoException{
		try {		
			//log.info("new request "+opName);
			Options options = new Options();
			ServiceClient sender = new ServiceClient();
			sender.setOptions(options);
			options.setTo(targetEPR);

			UUID id = UUID.randomUUID();
			String ids = "uuid:"+id;

			String newMsgBody=msgBody;
			Document doc = null;
			PolicyData pdReq = policyRequestDataMap.get(opName);

			if(pdReq!= null && SecurityUtil.isSecHeaderRequired(pdReq)){
				String header="";
				if(configuration!=null){
					OMElement[] headersList = configuration.getConfig();
					for(int i=0;i<headersList.length;i++){
						header = header + headersList[i].toString();
					}
				}
				String SOAPMSG = 
					"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" 
					+ "<soapenv:Envelope "
					+   "xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" "
					+   "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
					+   "xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" "
					+   "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" 
					+   "<soapenv:Header>" 
					+       header
					+   "</soapenv:Header>" 
					+   "<soapenv:Body wsu:Id=\"Body-1\">" 
					+       msgBody
					+   "</soapenv:Body>" 
					+ "</soapenv:Envelope>";

				try {
					doc = SOAPUtil.toSOAPPart(SOAPMSG);
				} catch (Exception e) {
					e.printStackTrace();
				}
				//OMElement secHeader = null;
				try {
					createSecurityHeader(doc,pdReq);
				} catch (SecurityException e) {
					e.printStackTrace();
				}
				//sender.addHeader(secHeader);
				SOAPEnvelope env = SAAJUtil.getSOAPEnvelopeFromDOOMDocument(doc);
				OMElement newHeader = null;
				try {
					newHeader = AXIOMUtil.stringToOM(env.getHeader().toString());
				} catch (XMLStreamException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				Iterator<OMElement> it = newHeader.getChildren();
				while(it.hasNext()){
					OMElement el = it.next();
					sender.addHeader(el);
				}
				//sender.addHeader(newHeader);
				SOAPBody body = env.getBody();
				newMsgBody = body.toString();
			}
			
			if (wsHeaderSupport){
				if (opName!=null){
					OMElement action = fac.createOMElement("Action", wsaNS);
					String actionValue = msgOpMap.get(opName).getOpAction();//def.getTargetNamespace()+"/"+portTypeName+"/"+opName;
					action.setText(actionValue);
					sender.addHeader(action);
					options.setAction(opName);
				}

				OMElement msgId = fac.createOMElement("MessageID", wsaNS);
				msgId.setText(ids);
				sender.addHeader(msgId);

				OMElement to = fac.createOMElement("To", wsaNS);
				to.setText(targetEPR.getAddress());
				sender.addHeader(to);

				msgs.put(ids,new LinkedList<MsgInfo>());

				if (wsAddressingSupport){
					OMElement replyTo = fac.createOMElement("ReplyTo", wsaNS);
					OMElement replyToAddress= fac.createOMElement("Address", wsaNS);
					replyToAddress.setText(localEPR.getAddress());
					replyTo.addChild(replyToAddress);
					sender.addHeader(replyTo);
				}
			}

			//The configuration information setted for this WSInterface will be added to
			//the header of the msg
			/*if(configuration!=null){
				if(configuration.isWsCoordConfigPresent())
					sender.addHeader(configuration.getWsCoordinationConf());

				if(configuration.isWsSecurityConfigPresent())
					sender.addHeader(configuration.getWsSecurityConf());
			}*/
			/*if(configuration!=null){
				OMElement[] headersList = configuration.getConfig();
				for(int i=0;i<headersList.length;i++){
					sender.addHeader(headersList[i]);
				}
			}*/
			
			if(pdReq == null && configuration!=null){
				OMElement[] headersList = configuration.getConfig();
				for(int i=0;i<headersList.length;i++){
					sender.addHeader(headersList[i]);
				}
			}
			
			try {
				OMElement el = AXIOMUtil.stringToOM(newMsgBody);
				sender.sendRobust(el);
				//Feedback param et
				sentMsgId.set(ids);
			} catch (Exception ex){
				failed(ex.toString());
			}
		} catch (AxisFault ex){
			failed(ex.toString());
		}
	}

	@OPERATION void oneWayOpWithParts(String opName, OpFeedbackParam<String> sentMsgId, Object[] msgParts){
		try{
			QName replyMsgName = msgOpMap.get(opName).getOpMsgName();
			//Message body construction: first creation of the wrapper with the message name
			OMElement contentWrapper = XMLib.getInstance().buildElement(replyMsgName);

			for(Object part : msgParts)contentWrapper.addChild(XMLib.getInstance().buildElementFromString((String)part));
			this.oneWayOp(opName, contentWrapper.toString(), sentMsgId);
		}
		catch(Exception ex){
			ex.printStackTrace();
			failed(ex.toString());
		}
	}
	
	@OPERATION void test(String prueba)
	{
		System.out.println(prueba);
	}

	@OPERATION void requestOp(String opName, String msgBody, OpFeedbackParam<WSMsgInfo> response, OpFeedbackParam<String> responseId) throws CartagoException{
		
		try {	
			Options options = new Options();
			ServiceClient sender = new ServiceClient();
			sender.setOptions(options);
			options.setTo(targetEPR);
			sender.setTargetEPR(targetEPR);
			
			UUID id = UUID.randomUUID();
			String ids = "uuid:"+id;
			responseId.set(ids);
			String newMsgBody=msgBody;
			Document doc = null;
			PolicyData pdReq = policyRequestDataMap.get(opName);
			
			if(pdReq!= null && SecurityUtil.isSecHeaderRequired(pdReq)){
				
				String header="";
				if(configuration!=null){
					OMElement[] headersList = configuration.getConfig();
					for(int i=0;i<headersList.length;i++){
						header = header + headersList[i].toString();
					}
				}
				String SOAPMSG = 
					"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" 
					+ "<soapenv:Envelope "
					+   "xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" "
					+   "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
					+   "xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" "
					+   "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" 
					+   "<soapenv:Header>" 
					+       header
					+   "</soapenv:Header>" 
					+   "<soapenv:Body wsu:Id=\"Body-1\">" 
					+       msgBody
					+   "</soapenv:Body>" 
					+ "</soapenv:Envelope>";

				try {
					doc = SOAPUtil.toSOAPPart(SOAPMSG);
				} catch (Exception e) { 
					e.printStackTrace(); 
				} 
				//OMElement secHeader = null;   
				try { 
					createSecurityHeader(doc,pdReq);
				} catch (Exception e) {
					e.printStackTrace();
				}
				//sender.addHeader(secHeader);
				SOAPEnvelope env = SAAJUtil.getSOAPEnvelopeFromDOOMDocument(doc);
				OMElement newHeader = null;
				try {
					newHeader = AXIOMUtil.stringToOM(env.getHeader().toString());
				} catch (XMLStreamException e) {
					e.printStackTrace();
				}
				Iterator<OMElement> it = newHeader.getChildren();
				while(it.hasNext()){
					OMElement el = it.next();
					sender.addHeader(el);
				}
				//sender.addHeader(newHeader);
				SOAPBody body = env.getBody();
				newMsgBody = body.toString();
			}
			
			if (wsHeaderSupport){
				if (opName!=null){
					OMElement action = fac.createOMElement("Action", wsaNS);
					String actionValue = msgOpMap.get(opName).getOpAction();;//def.getTargetNamespace()+"/"+portTypeName+"/"+opName+"Request";
					action.setText(actionValue);
					sender.addHeader(action);
					options.setAction("");
				}

				OMElement msgId = fac.createOMElement("MessageID", wsaNS);
				msgId.setText(ids);
				sender.addHeader(msgId);

				OMElement to = fac.createOMElement("To", wsaNS);
				to.setText(targetEPR.getAddress());
				sender.addHeader(to);
			}

			//Adding further WSInterface configuration information
			if(pdReq == null && configuration!=null){
				OMElement[] headersList = configuration.getConfig();
				for(int i=0;i<headersList.length;i++){
					sender.addHeader(headersList[i]);
				}
			}

			if (wsAddressingSupport){				
				OMElement replyTo = fac.createOMElement("ReplyTo", wsaNS);
				OMElement replyToAddress= fac.createOMElement("Address", wsaNS);
				replyToAddress.setText(localEPR.getAddress());
				replyTo.addChild(replyToAddress);
				sender.addHeader(replyTo);

				msgs.put(ids,new LinkedList<MsgInfo>());
				try {
					OMElement el = AXIOMUtil.stringToOM(newMsgBody);
					sender.sendRobust(el);
					
					log.info("[WS-INTERFACE] msg send ok - "+opName+" "+ids);
					//Waiting reply
					await("availableMsgs", ids, response, responseId);
					
					MsgInfo msg = msgs.get(ids).removeFirst();
					
					PolicyData pdResp = policyResponseDataMap.get(opName);
					
					if(pdResp!=null && SecurityUtil.isSecHeaderRequired(pdResp)){
						ValidationResults validationResults = validateMsg(msg, pdResp);
						WSMsgInfo msgValidated = validationResults.getMsg();
						response.set(msgValidated);
					} else {
						response.set(msg.getWSMsgInfo());
					}	

					log.info("[WS-INTERFACE] reply notified- ");

					/*boolean validationRes = validateMsg(msg, pdResp);
					if(validationRes==true){
						response.set(msg.getWSMsgInfo());
						log.info("[WS-INTERFACE] reply notified- ");
					}*/	
				} catch (Exception ex){System.out.println("AAAAAAAA");
				ex.printStackTrace();
					//failed(ex.toString());
				}
			} else {
				try {
					OMElement el = AXIOMUtil.stringToOM(newMsgBody);
					//log.info("[WS-INTERFACE] REQUEST MSG: \n"+msgBody);
					OMElement res = sender.sendReceive(el);
					//Conversione da stringa a envelope
					SOAPEnvelope env = factory.createSOAPEnvelope();
					env.addChild(res);
					//msgContext = new org.apache.axis2.context.MessageContext();
					//msgContext.setEnvelope(env);
					//MsgInfo msg = new MsgInfo(msgContext);
					//response.set(msg.getWSMsgInfo()); 
				} catch (Exception ex){
					failed(ex.toString());
				}
			}

		} catch (AxisFault ex){
			failed(ex.toString());
		}
	}

	@OPERATION void requestOpWithParts(String opName, OpFeedbackParam<WSMsgInfo> response, OpFeedbackParam<String> responseId, Object[] msgParts) throws IOException{
		try{
			QName replyMsgName = msgOpMap.get(opName).getOpMsgName();
			//Message body construction: first creation of the wrapper with the message name
			OMElement contentWrapper = XMLib.getInstance().buildElement(replyMsgName);

			//Adding all children provided by the user
			for(Object part : msgParts) contentWrapper.addChild(XMLib.getInstance().buildElementFromString((String)part));
			this.requestOp(opName, contentWrapper.toString(), response, responseId);
		}
		catch(Exception ex){
			//ex.printStackTrace();
			File f = new File(".");
			System.out.println(f.getCanonicalPath().toString());
			failed(ex.toString());
		}
	}

	// raw messaging

	@OPERATION void sendWSMsg(String opName, String msgName, String msgBody, OpFeedbackParam<String> sentMsgId){        
		sendSOAPMsg(msgName,msgBody,null,localEPR.getAddress(), sentMsgId);		
	}

	@OPERATION void sendWSMsgRelatedTo(String opName, String msgName, String msgBody, String relatedTo, OpFeedbackParam<String> sentMsgId){        
		sendSOAPMsg(msgName,msgBody,relatedTo,localEPR.getAddress(), sentMsgId);		
	}

	@OPERATION(guard="availableMsgs") void getWSReply(String id, OpFeedbackParam<WSMsgInfo> response, OpFeedbackParam<String> responseId){
		LinkedList<MsgInfo> envs = msgs.get(id);
		MsgInfo msg = envs.removeFirst();
		response.set(msg.getWSMsgInfo());
		responseId.set(id);
	}

	//

	@OPERATION void notifyMsg(MsgInfo msg){
		String msgid = msg.getWSARelatesTo();
		LinkedList<MsgInfo> envs = msgs.get(msgid);
		if (envs!=null){
			envs.add(msg);
			log("[WSINTERFACE:] Msg notified" + msg.getWSAAction() + " " + msg.getEnvelope().toString());
		} else {
			failed("msg unknown");
		}
	}

	@OPERATION void configure(ServiceConfiguration configuration){
		this.configuration = configuration;
	}

	public EndpointReference getLocalEPR() {
		return localEPR;
	}

	//GUARDS

	@GUARD boolean availableMsgs(String id, OpFeedbackParam<WSMsgInfo> feedback, OpFeedbackParam<String> replyId){
		LinkedList<MsgInfo> envs = msgs.get(id);
		if(envs ==null){
			msgs.put(id,new LinkedList<MsgInfo>());
			return false;
		}
		return !envs.isEmpty();
	}

	private void parseSecInfo(String wsdlURI){
		Policy policyService = null;
		Policy policyEndpoint = null;
		Policy policyOperation = null;
		Policy policyMessage = null;
		HashMap<String, Policy> messagePolicyMap = new HashMap<String, Policy>();
		XMLStreamReader reader = null;
		try {
			reader = XMLInputFactory.newInstance().createXMLStreamReader(new FileInputStream(wsdlURI));
		} catch (Exception e) {
			e.printStackTrace();
		}
		OMXMLParserWrapper builder = OMXMLBuilderFactory.createStAXOMBuilder(OMAbstractFactory.getOMFactory(),reader);

		OMElement element = builder.getDocumentElement();

		Iterator it = element.getChildElements();
		while(it.hasNext()){
			OMElement el = (OMElement) it.next();
			if(el.getLocalName().equals("service")){
				Iterator it2 = el.getChildrenWithName(new QName(Constants.URI_POLICY_NS, Constants.ELEM_POLICY_REF));
				while(it2.hasNext()){
					OMElement ref = (OMElement) it2.next();
					PolicyReference policyRef = PolicyEngine.getPolicyReference(ref);
					Policy policy = null; 
					Iterator it3  = builder.getDocumentElement().getChildrenWithName(new QName(Constants.URI_POLICY_NS, Constants.ELEM_POLICY)); 
					while(it3.hasNext()){
						policy = PolicyEngine.getPolicy((OMElement)it3.next());
						if(policy.getId().equals(policyRef.getURI().substring(1, policyRef.getURI().length()))){
							if(policyService == null){
								policyService = policy;
							} else {
								policyService = policyService.merge(policy);
							}
							break;
						}
					}
				}
				break;
			}
		}
		it = element.getChildElements();
		while(it.hasNext()){
			OMElement el = (OMElement) it.next();
			if(el.getLocalName().equals("binding")){
				Iterator it2 = el.getChildrenWithName(new QName(Constants.URI_POLICY_NS, Constants.ELEM_POLICY_REF));
				while(it2.hasNext()){
					OMElement ref = (OMElement) it2.next();
					PolicyReference policyRef = PolicyEngine.getPolicyReference(ref);
					Policy policy = null; 
					Iterator it3  = builder.getDocumentElement().getChildrenWithName(new QName(Constants.URI_POLICY_NS, Constants.ELEM_POLICY)); 
					while(it3.hasNext()){
						policy = PolicyEngine.getPolicy((OMElement)it3.next());
						if(policy.getId().equals(policyRef.getURI().substring(1, policyRef.getURI().length()))){
							if(policyEndpoint == null){
								policyEndpoint = policy;
							} else {
								policyEndpoint = policyEndpoint.merge(policy);
							}
							break;
						}
					}
				}
				Iterator it3 = el.getChildElements();
				while(it3.hasNext()){
					OMElement op = null;
					OMElement elemInsideBinding = (OMElement) it3.next();
					if(elemInsideBinding.getLocalName().equals("operation")){
						policyOperation = null;
						op = (OMElement) elemInsideBinding;
						Iterator it4 = op.getChildrenWithName(new QName(Constants.URI_POLICY_NS, Constants.ELEM_POLICY_REF));
						while(it4.hasNext()){
							OMElement ref = (OMElement) it4.next();
							PolicyReference policyRef = PolicyEngine.getPolicyReference(ref);
							Policy policy = null; 
							Iterator it5  = builder.getDocumentElement().getChildrenWithName(new QName(Constants.URI_POLICY_NS, Constants.ELEM_POLICY)); 
							while(it5.hasNext()){
								policy = PolicyEngine.getPolicy((OMElement)it5.next());
								if(policy.getId().equals(policyRef.getURI().substring(1, policyRef.getURI().length()))){
									if(policyOperation == null){
										policyOperation = policy;
									} else {
										policyOperation = policyOperation.merge(policy);
									}

									break;
								}
							}
						}
					}
					Iterator it6 = elemInsideBinding.getChildElements();
					while(it6.hasNext()){
						OMElement elemInsideOperation = (OMElement) it6.next();
						if(elemInsideOperation.getLocalName().equals("input")){
							policyMessage = null;
							Iterator it7 = elemInsideOperation.getChildrenWithName(new QName(Constants.URI_POLICY_NS, Constants.ELEM_POLICY_REF));
							while(it7.hasNext()){
								OMElement ref = (OMElement) it7.next();
								PolicyReference policyRef = PolicyEngine.getPolicyReference(ref);
								Policy policy = null; 
								Iterator it8  = builder.getDocumentElement().getChildrenWithName(new QName(Constants.URI_POLICY_NS, Constants.ELEM_POLICY)); 
								while(it8.hasNext()){
									policy = PolicyEngine.getPolicy((OMElement)it8.next());
									if(policy.getId().equals(policyRef.getURI().substring(1, policyRef.getURI().length()))){
										if(policyMessage == null){
											policyMessage = policy;
										} else {
											policyMessage = policyMessage.merge(policy);
										}
										break;
									}
								}
							}
							if(policyMessage == null){
								if(policyService != null && policyEndpoint != null && policyOperation != null){
									policyMessage = policyService;
									policyMessage = policyMessage.merge(policyEndpoint);
									policyMessage = policyMessage.merge(policyOperation);
								} else if(policyService != null && policyEndpoint != null && policyOperation == null){
									policyMessage = policyService;
									policyMessage = policyMessage.merge(policyEndpoint);
								} else if(policyService != null && policyEndpoint == null && policyOperation != null){
									policyMessage = policyService;
									policyMessage = policyMessage.merge(policyOperation);
								} else if(policyService == null && policyEndpoint != null && policyOperation != null){
									policyMessage = policyEndpoint;
									policyMessage = policyMessage.merge(policyOperation);
								} else if(policyService != null && policyEndpoint == null && policyOperation == null){
									policyMessage = policyService;
								} else if(policyService == null && policyEndpoint != null && policyOperation == null){
									policyMessage = policyEndpoint;
								} else if(policyService == null && policyEndpoint == null && policyOperation != null){
									policyMessage = policyOperation;
								}
							} else if(policyMessage != null){
								if(policyService != null && policyEndpoint != null && policyOperation != null){
									policyMessage = policyService.merge(policyEndpoint).merge(policyOperation).merge(policyOperation);
								} else if(policyService != null && policyEndpoint != null && policyOperation == null){
									policyMessage = policyService.merge(policyEndpoint).merge(policyMessage);
								} else if(policyService != null && policyEndpoint == null && policyOperation != null){
									policyMessage = policyService.merge(policyOperation).merge(policyMessage);
								} else if(policyService == null && policyEndpoint != null && policyOperation != null){
									policyMessage = policyEndpoint.merge(policyOperation).merge(policyMessage);
								} else if(policyService != null && policyEndpoint == null && policyOperation == null){
									policyMessage = policyService.merge(policyMessage);
								} else if(policyService == null && policyEndpoint != null && policyOperation == null){
									policyMessage = policyEndpoint.merge(policyMessage);
								} else if(policyService == null && policyEndpoint == null && policyOperation != null){
									policyMessage = policyOperation.merge(policyMessage);
								}
							}
							messagePolicyMap.put(op.getAttributeValue(new QName("name"))+"Request", policyMessage);
						}
						if(elemInsideOperation.getLocalName().equals("output")){
							policyMessage = null;
							Iterator it7 = elemInsideOperation.getChildrenWithName(new QName(Constants.URI_POLICY_NS, Constants.ELEM_POLICY_REF));
							while(it7.hasNext()){
								OMElement ref = (OMElement) it7.next();
								PolicyReference policyRef = PolicyEngine.getPolicyReference(ref);
								Policy policy = null; 
								Iterator it8  = builder.getDocumentElement().getChildrenWithName(new QName(Constants.URI_POLICY_NS, Constants.ELEM_POLICY)); 
								while(it8.hasNext()){
									policy = PolicyEngine.getPolicy((OMElement)it8.next());
									if(policy.getId().equals(policyRef.getURI().substring(1, policyRef.getURI().length()))){
										if(policyMessage == null){
											policyMessage = policy;
										} else {
											policyMessage = policyMessage.merge(policy);
										}
										break;
									}
								}
							}
							if(policyMessage == null){
								if(policyService != null && policyEndpoint != null && policyOperation != null){
									policyMessage = policyService;
									policyMessage = policyMessage.merge(policyEndpoint);
									policyMessage = policyMessage.merge(policyOperation);
								} else if(policyService != null && policyEndpoint != null && policyOperation == null){
									policyMessage = policyService;
									policyMessage = policyMessage.merge(policyEndpoint);
								} else if(policyService != null && policyEndpoint == null && policyOperation != null){
									policyMessage = policyService;
									policyMessage = policyMessage.merge(policyOperation);
								} else if(policyService == null && policyEndpoint != null && policyOperation != null){
									policyMessage = policyEndpoint;
									policyMessage = policyMessage.merge(policyOperation);
								} else if(policyService != null && policyEndpoint == null && policyOperation == null){
									policyMessage = policyService;
								} else if(policyService == null && policyEndpoint != null && policyOperation == null){
									policyMessage = policyEndpoint;
								} else if(policyService == null && policyEndpoint == null && policyOperation != null){
									policyMessage = policyOperation;
								}
							} else if(policyMessage != null){
								if(policyService != null && policyEndpoint != null && policyOperation != null){
									policyMessage = policyService.merge(policyEndpoint).merge(policyOperation).merge(policyMessage);
								} else if(policyService != null && policyEndpoint != null && policyOperation == null){
									policyMessage = policyService.merge(policyEndpoint).merge(policyMessage);
								} else if(policyService != null && policyEndpoint == null && policyOperation != null){
									policyMessage = policyService.merge(policyOperation).merge(policyMessage);
								} else if(policyService == null && policyEndpoint != null && policyOperation != null){
									policyMessage = policyEndpoint.merge(policyOperation).merge(policyMessage);
								} else if(policyService != null && policyEndpoint == null && policyOperation == null){
									policyMessage = policyService.merge(policyMessage);
								} else if(policyService == null && policyEndpoint != null && policyOperation == null){
									policyMessage = policyEndpoint.merge(policyMessage);
								} else if(policyService == null && policyEndpoint == null && policyOperation != null){
									policyMessage = policyOperation.merge(policyMessage);
								}
							}
							messagePolicyMap.put(op.getAttributeValue(new QName("name"))+"Response", policyMessage);

						}
					}
				}
			}
		}
		Set list  = msgOpMap.keySet();
		Iterator iter = list.iterator();

		while(iter.hasNext()) {
			String key = (String) iter.next();
			Policy policy = null;
			try {
				policy = messagePolicyMap.get(key+"Request");
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			if(policy!=null){
				policy = (Policy) policy.normalize(true);
				ExactlyOne eo =  (ExactlyOne) policy.getFirstPolicyComponent();
				List listOfPolicyAlternatives = eo.getPolicyComponents();
				All aPolicyAlternative = (All) listOfPolicyAlternatives.get(0);
				List listOfAssertions = null;
				listOfAssertions = aPolicyAlternative.getAssertions();
				PolicyData policyData = null;
				try {
					policyData = PolicyBuilder.build(listOfAssertions);
				} catch (WSSPolicyException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				policyRequestDataMap.put(key, policyData);
			}
			try {
				policy = messagePolicyMap.get(key+"Response");
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			if(policy!=null){
				policy = (Policy) policy.normalize(true);
				ExactlyOne eo =  (ExactlyOne) policy.getFirstPolicyComponent();
				List listOfPolicyAlternatives = eo.getPolicyComponents();
				All aPolicyAlternative = (All) listOfPolicyAlternatives.get(0);
				List listOfAssertions = null;
				listOfAssertions = aPolicyAlternative.getAssertions();
				PolicyData policyData = null;
				try {
					policyData = PolicyBuilder.build(listOfAssertions);
				} catch (WSSPolicyException e) {
					e.printStackTrace();
				}
				policyResponseDataMap.put(key, policyData);	
			}
		}
	}

	private void createSecurityHeader(Document doc, PolicyData pd) throws SecurityException, CartagoException{

		//SecurityHeader
		WSSecHeader secHeader = new WSSecHeader();
		secHeader.setMustUnderstand(false);
		secHeader.insertSecurityHeader(doc);

		SecurityInfo securityConfig = getSecurityInfo();
    	MessageData md = new MessageData(pd,doc,secHeader,securityConfig,null,true);

		//TransportBinding
		if(pd.isTransportBinding()) {
			//log.debug("Building transport binding");
			//TransportBindingBuilder building = new TransportBindingBuilder();
			//building.build(md);

		//SymmericBinding
		} else if(pd.isSymmetricBinding()) {
			//log.debug("Building SymmetricBinding");
			//SymmetricBindingBuilder builder = new SymmetricBindingBuilder();
			//builder.build(md);

		//AsymmetricBinding
		} else {
			AsymmetricBindingBuilder builder = new AsymmetricBindingBuilder();
			try {
				builder.build(md);
			} catch (SecurityException e) {
				
				File f = new File(".");
				try {
					System.out.println("LOGGGGGGGG" + f.getCanonicalPath().toString());
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				e.printStackTrace();
			}
		}
	}
	
	private ValidationResults validateMsg(MsgInfo msg, PolicyData pd) throws CartagoException{
		OpFeedbackParam<ValidationResults> res = new OpFeedbackParam<ValidationResults>();
		if(pd !=null && SecurityUtil.isSecHeaderRequired(pd)){
			WSSecurityOperation operation = new WSSecurityOperation(WSSecurityOperation.VALIDATE_MSG);
			WSSecurityOperationContent opContent = new WSSecurityOperationContent(pd, true);
			try {
	    		execLinkedOp("out-port","validateMessage",targetEPR.getAddress(), msg.getWSMsgInfo(), operation, new DataContainer(DataContainer.OPERATION_SPECIFIC, opContent), res);
	    	} catch (Exception ex){
	    		ex.printStackTrace();
	    	}
		}
		return res.get();
	}
	
	private SecurityInfo getSecurityInfo() throws CartagoException{
		OpFeedbackParam<HashMap<String, DataContainer>> res = new OpFeedbackParam<HashMap<String,DataContainer>>();
    	
		execLinkedOp("out-port","getWalletInfo",securityTargetEPR.getAddress(),new Integer(IOperation.WS_SECURITY), res);
    
    	HashMap<String, DataContainer> resSecurity = res.get();
    	Set list = resSecurity.keySet();

    	SecurityInfo securityInfo = null;
    	for(Object key:list){
			DataContainer value = resSecurity.get(key);
			securityInfo = (SecurityInfo) value.getData();
			break;
		}
		return securityInfo;
	}
	
	class MsgOpMapEntry{

		private String opAction;//Poi non cosi' utile
		private QName opMsgName;
		
		public MsgOpMapEntry(String opAction, QName opMsgName) {
			this.opAction = opAction;
			this.opMsgName = opMsgName;
		}

		public String getOpAction() {
			return opAction;
		}

		public QName getOpMsgName() {
			return opMsgName;
		}
	}

	/** 
	 * From the WSDL specification:
	 * 
	 * The soap:body element specifies how the message parts appear inside the SOAP Body element.
	 * 
	 * The parts of a message may either be abstract type definitions, or concrete schema definitions. If abstract definitions,
	 *  the types are serialized according to some set of rules defined by an encoding style. 
	 *  Each encoding style is identified using a list of URIs, as in the SOAP specification.
	 *  The soap:body binding element provides information on how to assemble the different message parts inside the Body element of the SOAP message. The soap:body element is used in both RPC-oriented and document-oriented messages, but the style of the enclosing operation has important effects on how the Body section is structured:
	 *  	- If the operation style is rpc each part is a parameter or a return value and appears inside a wrapper element within 
	 *        the body (following Section 7.1 of the SOAP specification). 
	 *        The wrapper element is named identically to the operation name and its namespace is the value of the namespace attribute. 
	 *        Each message part (parameter) appears under the wrapper, represented by an accessor named identically to the corresponding 
	 *        parameter of the call. Parts are arranged in the same order as the parameters of the call.
	 *     -  If the operation style is document there are no additional wrappers, and the message parts appear directly under the SOAP Body
	 *        element. 
	 * 
	 * So, for providing the name for the wrapper we use the convention related to the operations names:
	 * 
	 *  The name attribute of the input and output elements provides a unique name among all input and output elements 
	 *  within the enclosing port type. In order to avoid having to name each input and output element within an operation, 
	 *  WSDL provides some default values based on the operation name. If the name attribute is not specified on a one-way or 
	 *  notification message, it defaults to the name of the operation. If the name attribute is not specified on the input 
	 *  or output messages of a request-response or solicit-response operation, the name defaults to the name of the operation 
	 *  with "Request"/"Solicit" or "Response" appended, respectively.
	 */

	private QName getInOpMsgName(boolean isBindingRpc, Operation op){
		Input in = op.getInput();
		QName opInMsgName = null;

		//RPC binding: the message name is the name of the operation 
		if(isBindingRpc)
			opInMsgName = new QName(op.getName());
		/*
		 * Document binding: construction of the message name:
		 * - if a name is provided for the input message we use this
		 * - if a name is not provided is used the name of the operation
		 */
		else{
			opInMsgName = in.getMessage().getQName();
			if(opInMsgName == null)
				opInMsgName = new QName(op.getName());	
		}
		return opInMsgName;
	}
}